package cn.wolfcode.web.controller;

import cn.wolfcode.common.constants.CommonConstants;
import cn.wolfcode.common.domain.UserInfo;
import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CommonCodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.common.web.anno.RequireLogin;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMessage;
import cn.wolfcode.redis.CommonRedisKey;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ConcurrentHashMap;


@RestController
@RequestMapping("/order")
@Slf4j
public class OrderInfoController {

    private static final ConcurrentHashMap<Long, Boolean> STOCK_OVER_MAP = new ConcurrentHashMap<>();

    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IOrderInfoService orderInfoService;


    public static void removeStockOverFlag(Long seckillId) {
        STOCK_OVER_MAP.remove(seckillId);
    }

    @RequireLogin
    @GetMapping("/find")
    public Result<OrderInfo> findById(String orderNo, @RequestHeader(CommonConstants.TOKEN_NAME) String token) {
        UserInfo user = this.getUserByToken(token);
        OrderInfo orderInfo = orderInfoService.findByIdFromRedis(orderNo);
        if (!orderInfo.getUserId().equals(user.getPhone())) {
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }
        return Result.success(orderInfo);
    }

    /**
     * 第一次性能测试：
     * 请求次数：15000
     * 线程数：100
     * 测试情况：150/s   => 300/s    => 800/s
     */
    @RequireLogin
    @RequestMapping("/doSeckill")

    public Result<String> doSeckill(Integer time, Long seckillId, @RequestHeader(CommonConstants.TOKEN_NAME) String token) {
        String seckillOrderKey = "";

        // 通过本地 JVM MAP 判断库存是否已售完
        if (Boolean.TRUE.equals(STOCK_OVER_MAP.get(seckillId))) {
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }

        // 1. 基于 token 获取到用户信息(必须登录)
        UserInfo userInfo = this.getUserByToken(token);

        try {
            // 2. 基于场次+秒杀id获取到秒杀商品对象
            SeckillProductVo vo = seckillProductService.selectByIdAndTime(seckillId, time);
            if (vo == null) {
                throw new BusinessException(SeckillCodeMsg.REMOTE_DATA_ERROR);
            }
            // 3. 判断时间是否大于开始时间 && 小于 开始时间+2小时
        /*if (!DateUtil.isLegalTime(vo.getStartDate(), time)) {
            throw new BusinessException(SeckillCodeMsg.OUT_OF_SECKILL_TIME_ERROR);
        }*/
            // 4. 判断用户是否重复下单
            // 基于用户 + 秒杀 id + 场次查询订单, 如果存在订单, 说明用户已经下过单
            // 从redis判断用户是否重复下单
            seckillOrderKey = SeckillRedisKey.SECKILL_ORDER_HASH.join(seckillId + "");
            Long increment = redisTemplate.opsForHash().increment(seckillOrderKey, userInfo.getPhone() + "", 1);
            if (increment > 2) {
                throw new BusinessException(SeckillCodeMsg.REPEAT_SECKILL);
            }
            // 5. 判断库存是否充足
            String realCountKey = SeckillRedisKey.SECKILL_REAL_COUNT_HASH.join(time + "");
            // 扣减库存以后剩余的库存
            Long remainStockCount = redisTemplate.opsForHash().increment(realCountKey, seckillId + "", -1);
            if (remainStockCount < 0) {
                // 在 JVM 本地 MAP 中标记该秒杀商品库存已售完
                STOCK_OVER_MAP.put(seckillId, true);
                throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
            }
            // 6. 执行下单操作(减少库存, 创建订单)
            // orderInfoService.doSeckill(userInfo, vo);
            // 消息内容：用户id、秒杀id、场次
            // 目的地： 主题:标签       ORDER_CREATE:PENDING
            OrderMessage message = new OrderMessage(time, seckillId, userInfo.getPhone(), token);
            SendResult result = rocketMQTemplate.syncSend(MQConstant.ORDER_PENDING_TOPIC, message);
            log.info("[创建订单] 发送创建订单消息结果：{}", result.getMsgId());
        } catch (BusinessException e) {
            // 判断当前异常是否是重复下单异常，不是重复下单异常我们才处理
            if (e.getCodeMsg() == SeckillCodeMsg.SECKILL_STOCK_OVER) {
                // 对本次记录用户重复下单标识-1
                redisTemplate.opsForHash().increment(seckillOrderKey, userInfo.getPhone() + "", -1);
            }
            throw e;
        }
        return Result.success("正在排队下单中...");
    }

    private UserInfo getUserByToken(String token) {
        return JSON.parseObject(redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)), UserInfo.class);
    }
}
